#include <linux/slab.h>
//#include <linux/dma-mapping.h>

#include "fh_drv_cipher.h"
#include "hw_rpu.h"
#include "fh_error_mpi.h"




FH_S32 FH_DRV_CIPHER_CreateHandle(FH_HANDLE *pHdlr, FH_VOID *p_priv)
{	
	CESA_RES_S *cesa = (CESA_RES_S *)p_priv;
	DRV_CIPHER_HDLR_S *hwCtrl;

	if ( NULL == pHdlr)
        return FH_ERR_CIPHER_INVALID_POINT;
	
	*pHdlr = (FH_HANDLE)kzalloc(sizeof(DRV_CIPHER_HDLR_S), GFP_KERNEL);
	if (0 == *pHdlr)
		return FH_ERR_CIPHER_FAILED_GETHANDLE;

	hwCtrl = (DRV_CIPHER_HDLR_S *)(*pHdlr);	
	hwCtrl->priv = p_priv;
	hwCtrl->regs = cesa->regs + RPU_REG_OFFSET;

	return FH_SUCCESS;
}

FH_S32 FH_DRV_CIPHER_DestroyHandle(FH_HANDLE Hdlr)
{
	DRV_CIPHER_HDLR_S *hwCtrl = (DRV_CIPHER_HDLR_S *)Hdlr;
	CESA_RES_S *cesa = (CESA_RES_S *)hwCtrl->priv;

	
	if (0 == Hdlr) {
		return FH_ERR_CIPHER_INVALID_HANDLE;
	}

	if (cesa->curr_hdlr == hwCtrl)
		cesa->curr_hdlr = NULL;
	
	kfree((void *)Hdlr);
	
	return FH_SUCCESS;
}

DRV_RPU_ST_E rpu_st_switch(void __iomem *regbase, DRV_RPU_ST_E old, DRV_RPU_ST_E new)
{
	if (old != new)
	{
		if (new & 0x3)
		{
			if (0 == (old & 0x3))
			{
				rpu_aes_sha256_rsa2048_inst_init(regbase);
			}
			
			switch(new)
			{
			case RPU_ST_AES128_SH256_RSA2048_4_AES128:
				rpu_aes_init(regbase);
				return RPU_ST_AES128_SH256_RSA2048_4_AES128;
			case RPU_ST_AES128_SH256_RSA2048_4_SH256:
				rpu_sha256_init(regbase);
				return RPU_ST_AES128_SH256_RSA2048_4_SH256;
			case RPU_ST_AES128_SH256_RSA2048_4_RSA2048:
				rpu_rsa2048_init(regbase);
				return RPU_ST_AES128_SH256_RSA2048_4_RSA2048;
			default:
				break;
			}
		}

		if (new & 0x1C)
		{
			if (0 == (old & 0x1C))
			{
				rpu_sm2_sm3_sm4_inst_init(regbase);
			}
			
			switch(new)
			{
			case RPU_ST_SM2_SM3_SM4_4_SM4:
				rpu_sm4_init(regbase);
				return RPU_ST_SM2_SM3_SM4_4_SM4;
			case RPU_ST_SM2_SM3_SM4_4_SM3:
				rpu_sm3_init(regbase);
				return RPU_ST_SM2_SM3_SM4_4_SM3;
			case RPU_ST_SM2_SM3_SM4_4_SM2_VERIFY:
				rpu_sm2_verify_init(regbase);
				return RPU_ST_SM2_SM3_SM4_4_SM2_VERIFY;
			case RPU_ST_SM2_SM3_SM4_4_SM2_SIGN:
				rpu_sm2_sign_init(regbase);
				return RPU_ST_SM2_SM3_SM4_4_SM2_SIGN;
			default:
				break;
			}
		}

		switch(new)
		{
		case RPU_ST_DES:
			rpu_des_init(regbase);
			return RPU_ST_DES;
		case RPU_ST_TDES:
			rpu_tdes_init(regbase);
			return RPU_ST_TDES;
		case RPU_ST_SH1:
			rpu_sha1_init(regbase);
			return RPU_ST_SH1;
		default:
			break;
		}
	}

	if (old == new)
		return old;

	return RPU_ST_BUTT;
}

/* This static function should be called only by
 * FH_DRV_CIPHER_Config
 * FH_DRV_CIPHER_Encrypt
 * FH_DRV_CIPHER_Encrypt
 */
static FH_S32 Cipher_HdlrCheck_BreakCheck(DRV_CIPHER_HDLR_S *hwCtrl, DRV_RPU_STAGE_E Stage)
{
	CESA_RES_S *cesa = (CESA_RES_S *)hwCtrl->priv;
	void __iomem *regbase = hwCtrl->regs;

	if (cesa->curr_hdlr != (void *)hwCtrl) {
		if (cesa->curr_hdlr != NULL)
			((DRV_CIPHER_HDLR_S *)(cesa->curr_hdlr))->is_break = true;

		cesa->curr_hdlr = (void *)hwCtrl;
	}

	if (RPU_STAGE_CONFIG == Stage) {			//如果这里跑了
		hwCtrl->is_break = false;
		switch(hwCtrl->stage)
		{
		case RPU_STAGE_CREATE:
		case RPU_STAGE_CONFIG:
		case RPU_STAGE_DATA:
			hwCtrl->stage = RPU_STAGE_CONFIG;
			break;
		default:
			return FH_ERR_CIPHER_FAILED_CHECKSTAGE;
		}
	}
	
	if (hwCtrl->is_break) {						//这里一定不跑，一定是在做data处理时才会跑这里

		switch(Stage)
		{
		case RPU_STAGE_DATA:
			if (RPU_STAGE_CONFIG == hwCtrl->stage)
				hwCtrl->stage = RPU_STAGE_DATA;
			break;
		default:
			return FH_ERR_CIPHER_FAILED_CHECKSTAGE;
		}

		if (hwCtrl->st != rpu_st_switch(regbase, cesa->st, hwCtrl->st))
			return FH_ERR_CIPHER_FAILED_CONFIGINS;

		cesa->st = hwCtrl->st;
		
		if (rpu_key_setup_wait(regbase, (uint8_t *)hwCtrl->key, hwCtrl->key_len, 100))
			return FH_ERR_CIPHER_FAILED_CONFIGKEY;
		
		if (CBC_MODE == hwCtrl->work_mode) {
			if (rpu_iv_setup_imm(regbase, (uint8_t *)hwCtrl->iv, hwCtrl->iv_len))
				return FH_ERR_CIPHER_FAILED_CONFIGIV;
		}

		hwCtrl->is_init = true;
		hwCtrl->is_break = false;

	} else {									//这里一定会跑

		if (RPU_STAGE_DATA == Stage) {	//这里一定不跑，一定是在做data处理时才会跑这里
			switch(hwCtrl->stage)
			{
			case RPU_STAGE_CONFIG:
				hwCtrl->is_init = (hwCtrl->IVSet) ? true : false;
				break;
			case RPU_STAGE_DATA:
				hwCtrl->is_init = false;
				break;
			default:
				return FH_ERR_CIPHER_FAILED_CHECKSTAGE;
			}

			hwCtrl->stage = RPU_STAGE_DATA;
		}
	}

	return FH_SUCCESS;
}

FH_S32 FH_DRV_CIPHER_Config(FH_HANDLE Hdlr,  FH_UNF_CIPHER_CTRL_S *pConfig)
{
	DRV_CIPHER_HDLR_S *hwCtrl = (DRV_CIPHER_HDLR_S *)Hdlr;
	CESA_RES_S *cesa = (CESA_RES_S *)hwCtrl->priv;
	void __iomem *regbase = hwCtrl->regs;
	FH_S32 ret = FH_SUCCESS;

	

	mutex_lock(&cesa->lock);

	hwCtrl->IVSet = pConfig->stChangeFlags.bit1IV;
	Cipher_HdlrCheck_BreakCheck(hwCtrl, RPU_STAGE_CONFIG);

	if (pConfig->enAlg == FH_UNF_CIPHER_ALG_AES
		&& pConfig->enWorkMode <= FH_UNF_CIPHER_WORK_MODE_CBC
		//&& pConfig->enBitWidth == FH_UNF_CIPHER_BIT_WIDTH_128BIT
		&& pConfig->enKeyLen == FH_UNF_CIPHER_KEY_AES_128BIT
		&& pConfig->enKeySrc == FH_UNF_CIPHER_KEY_SRC_USER) {

		hwCtrl->st = rpu_st_switch(regbase, cesa->st, RPU_ST_AES128_SH256_RSA2048_4_AES128);
		hwCtrl->algo = RPU_ALGO_AES128;
		hwCtrl->key_len = 16;
		hwCtrl->iv_len = 16;
	} else if (pConfig->enAlg == FH_UNF_CIPHER_ALG_DES
		&& pConfig->enWorkMode <= FH_UNF_CIPHER_WORK_MODE_CBC
		//&& pConfig->enBitWidth == FH_UNF_CIPHER_BIT_WIDTH_64BIT
		&& pConfig->enKeyLen == FH_UNF_CIPHER_KEY_DES_64BIT
		&& pConfig->enKeySrc == FH_UNF_CIPHER_KEY_SRC_USER) {

		hwCtrl->st = rpu_st_switch(regbase, cesa->st, RPU_ST_DES);
		hwCtrl->algo = RPU_ALGO_DES;
		hwCtrl->key_len = 8;
		hwCtrl->iv_len = 8;
	} else if (pConfig->enAlg == FH_UNF_CIPHER_ALG_3DES
		&& pConfig->enWorkMode <= FH_UNF_CIPHER_WORK_MODE_CBC
		//&& pConfig->enBitWidth == FH_UNF_CIPHER_BIT_WIDTH_64BIT
		&& pConfig->enKeyLen == FH_UNF_CIPHER_KEY_DES_192BIT
		&& pConfig->enKeySrc == FH_UNF_CIPHER_KEY_SRC_USER) {

		hwCtrl->st = rpu_st_switch(regbase, cesa->st, RPU_ST_TDES);
		hwCtrl->algo = RPU_ALGO_TDES;
		hwCtrl->key_len = 24;
		hwCtrl->iv_len = 8;
	} else if (pConfig->enAlg == FH_UNF_CIPHER_ALG_SM4
		&& pConfig->enWorkMode <= FH_UNF_CIPHER_WORK_MODE_CBC
		//&& pConfig->enBitWidth == FH_UNF_CIPHER_BIT_WIDTH_128BIT
		&& pConfig->enKeyLen == FH_UNF_CIPHER_KEY_SM4_128BIT
		&& pConfig->enKeySrc == FH_UNF_CIPHER_KEY_SRC_USER) {

		hwCtrl->st = rpu_st_switch(regbase, cesa->st, RPU_ST_SM2_SM3_SM4_4_SM4);
		hwCtrl->algo = RPU_ALGO_SM4;
		hwCtrl->key_len = 16;
		hwCtrl->iv_len = 16;
	} else {
		ret = FH_ERR_CIPHER_INVALID_PARA;
		goto cfg_done;
	}
	
	if (hwCtrl->st) {
		cesa->st = hwCtrl->st;

		memcpy(hwCtrl->key, pConfig->u32Key, hwCtrl->key_len);
		ret = rpu_key_setup_wait(regbase, (uint8_t *)pConfig->u32Key, hwCtrl->key_len, 100);
		if (ret) {
			ret = FH_ERR_CIPHER_FAILED_CONFIGKEY;
			goto cfg_done;
		}

		
		if(pConfig->enWorkMode == FH_UNF_CIPHER_WORK_MODE_CBC) {
			hwCtrl->work_mode = CBC_MODE;
			if (pConfig->stChangeFlags.bit1IV) {
				memcpy(hwCtrl->iv, pConfig->u32IV, hwCtrl->iv_len);
				ret = rpu_iv_setup_imm(regbase, (uint8_t *)pConfig->u32IV, hwCtrl->iv_len);
				if (ret) {
					ret = FH_ERR_CIPHER_FAILED_CONFIGIV;
					goto cfg_done;
				}
			}
		} else {
			hwCtrl->work_mode = ECB_MODE;
		}
	}
	else
		ret = FH_ERR_CIPHER_FAILED_CONFIGINS;




cfg_done:
	mutex_unlock(&cesa->lock);
	return ret;
}

FH_S32 FH_DRV_CIPHER_Encrypt(FH_HANDLE Hdlr, FH_U32 ScrPhyAddr, FH_U32 DestPhyAddr, FH_U32 u32DataLength)
{
	DRV_CIPHER_HDLR_S *hwCtrl = (DRV_CIPHER_HDLR_S *)Hdlr;
	CESA_RES_S *cesa = (CESA_RES_S *)hwCtrl->priv;
	void __iomem *regbase = hwCtrl->regs;
	FH_S32 ret = FH_SUCCESS;

	mutex_lock(&cesa->lock);

	ret = Cipher_HdlrCheck_BreakCheck(hwCtrl, RPU_STAGE_DATA);
	if (ret)
		goto enc_done;
	
	switch(hwCtrl->algo)
	{
	case RPU_ALGO_AES128:
		ret = rpu_aes_crypto_irq(
			regbase,
			(uint8_t *)ScrPhyAddr,
			u32DataLength,
			(uint8_t *)DestPhyAddr,
			ENCRYPT_OPR,
			hwCtrl->work_mode,
			hwCtrl->is_init);
		break;
	case RPU_ALGO_DES:
		ret = rpu_des_crypto_irq(
			regbase,
			(uint8_t *)ScrPhyAddr,
			u32DataLength,
			(uint8_t *)DestPhyAddr,
			ENCRYPT_OPR,
			hwCtrl->work_mode,
			hwCtrl->is_init);
		break;
	case RPU_ALGO_TDES:
		ret = rpu_tdes_crypto_irq(
			regbase,
			(uint8_t *)ScrPhyAddr,
			u32DataLength,
			(uint8_t *)DestPhyAddr,
			ENCRYPT_OPR,
			hwCtrl->work_mode,
			hwCtrl->is_init);
		break;
	case RPU_ALGO_SM4:
		ret = rpu_sm4_crypto_irq(
			regbase,
			(uint8_t *)ScrPhyAddr,
			u32DataLength,
			(uint8_t *)DestPhyAddr,
			ENCRYPT_OPR,
			hwCtrl->work_mode,
			hwCtrl->is_init);
		break;
	default:
		ret = FH_ERR_CIPHER_INVALID_PARA;
		goto enc_done;
	}

	if (ret) {
		ret = FH_ERR_CIPHER_FAILED_ENCRYPT;
	} else {
		wait_for_completion(&cesa->completion);
		reinit_completion(&cesa->completion);
		rpu_iv_get_imm(regbase, (uint8_t *)hwCtrl->iv, hwCtrl->iv_len);
		//dma_sync_single_for_cpu(cesa->dev, (dma_addr_t)DestPhyAddr, u32DataLength, DMA_BIDIRECTIONAL);
	}

enc_done:
	mutex_unlock(&cesa->lock);
	return ret;
}

FH_S32 FH_DRV_CIPHER_Decrypt(FH_HANDLE Hdlr, FH_U32 ScrPhyAddr, FH_U32 DestPhyAddr, FH_U32 u32DataLength)
{
	DRV_CIPHER_HDLR_S *hwCtrl = (DRV_CIPHER_HDLR_S *)Hdlr;
	CESA_RES_S *cesa = (CESA_RES_S *)hwCtrl->priv;
	void __iomem *regbase = hwCtrl->regs;
	FH_S32 ret = FH_SUCCESS;

	mutex_lock(&cesa->lock);

	ret = Cipher_HdlrCheck_BreakCheck(hwCtrl, RPU_STAGE_DATA);
	if (ret)
		goto dec_done;
	
	switch(hwCtrl->algo)
	{
	case RPU_ALGO_AES128:
		ret = rpu_aes_crypto_irq(
			regbase,
			(uint8_t *)ScrPhyAddr,
			u32DataLength,
			(uint8_t *)DestPhyAddr,
			DECRYPT_OPR,
			hwCtrl->work_mode,
			hwCtrl->is_init);
		break;
	case RPU_ALGO_DES:
		ret = rpu_des_crypto_irq(
			regbase,
			(uint8_t *)ScrPhyAddr,
			u32DataLength,
			(uint8_t *)DestPhyAddr,
			DECRYPT_OPR,
			hwCtrl->work_mode,
			hwCtrl->is_init);
		break;
	case RPU_ALGO_TDES:
		ret = rpu_tdes_crypto_irq(
			regbase,
			(uint8_t *)ScrPhyAddr,
			u32DataLength,
			(uint8_t *)DestPhyAddr,
			DECRYPT_OPR,
			hwCtrl->work_mode,
			hwCtrl->is_init);
		break;
	case RPU_ALGO_SM4:
		ret = rpu_sm4_crypto_irq(
			regbase,
			(uint8_t *)ScrPhyAddr,
			u32DataLength,
			(uint8_t *)DestPhyAddr,
			DECRYPT_OPR,
			hwCtrl->work_mode,
			hwCtrl->is_init);
		break;
	default:
		ret = FH_ERR_CIPHER_INVALID_PARA;
		goto dec_done;
	}

	if (ret) {
		ret = FH_ERR_CIPHER_FAILED_DECRYPT;
	} else {
		wait_for_completion(&cesa->completion);
		reinit_completion(&cesa->completion);
		rpu_iv_get_imm(regbase, (uint8_t *)hwCtrl->iv, hwCtrl->iv_len);
		//dma_sync_single_for_cpu(cesa->dev, (dma_addr_t)DestPhyAddr, u32DataLength, DMA_BIDIRECTIONAL);
	}

dec_done:
	mutex_unlock(&cesa->lock);
	return ret;
}



FH_S32 FH_DRV_CIPHER_EncryptMulti(FH_HANDLE Hdlr, FH_UNF_CIPHER_DATA_S *DataPkg, FH_U32 DataPkgNum)
{
	int i,ret;

	for (i = 0; i < DataPkgNum; i++) {
		ret = FH_DRV_CIPHER_Encrypt(Hdlr, DataPkg->u32SrcPhyAddr, DataPkg->u32DestPhyAddr, DataPkg->u32ByteLength);
		if (ret)
			break;
	}
	
	return ret;
}

FH_S32 FH_DRV_CIPHER_DecryptMulti(FH_HANDLE Hdlr, FH_UNF_CIPHER_DATA_S *DataPkg, FH_U32 DataPkgNum)
{
	int i,ret;

	for (i = 0; i < DataPkgNum; i++) {
		ret = FH_DRV_CIPHER_Decrypt(Hdlr, DataPkg->u32SrcPhyAddr, DataPkg->u32DestPhyAddr, DataPkg->u32ByteLength);
		if (ret)
			break;
	}
	
	return ret;
}

FH_S32 FH_DRV_CIPHER_GetHandleConfig(FH_HANDLE Hdlr, FH_UNF_CIPHER_CTRL_S *pConfig)
{
	DRV_CIPHER_HDLR_S *hwCtrl = (DRV_CIPHER_HDLR_S *)Hdlr;

	memcpy(pConfig->u32Key, hwCtrl->key, hwCtrl->key_len);
	memcpy(pConfig->u32IV, hwCtrl->iv, hwCtrl->iv_len);

	switch(hwCtrl->algo)
	{
	case RPU_ALGO_AES128:
		pConfig->enAlg = FH_UNF_CIPHER_ALG_AES;
		pConfig->enKeyLen = FH_UNF_CIPHER_KEY_AES_128BIT;
		break;
	case RPU_ALGO_DES:
		pConfig->enAlg = FH_UNF_CIPHER_ALG_DES;
		pConfig->enKeyLen = FH_UNF_CIPHER_KEY_DES_64BIT;
		break;
	case RPU_ALGO_TDES:
		pConfig->enAlg = FH_UNF_CIPHER_ALG_3DES;
		pConfig->enKeyLen = FH_UNF_CIPHER_KEY_DES_192BIT;
		break;
	case RPU_ALGO_SM4:
		pConfig->enAlg = FH_UNF_CIPHER_ALG_SM4;
		pConfig->enKeyLen = FH_UNF_CIPHER_KEY_SM4_128BIT;
		break;
	default:
		pConfig->enAlg = FH_UNF_CIPHER_ALG_BUTT;
		pConfig->enKeyLen = FH_UNF_CIPHER_KEY_BUTT;
	}

	switch(hwCtrl->work_mode)
	{
	case ECB_MODE:
		pConfig->enWorkMode = FH_UNF_CIPHER_WORK_MODE_ECB;
		break;
	case CBC_MODE:
		pConfig->enWorkMode = FH_UNF_CIPHER_WORK_MODE_CBC;
		break;
	default:
		pConfig->enWorkMode = FH_UNF_CIPHER_WORK_MODE_BUTT;
	}

	pConfig->enKeySrc = FH_UNF_CIPHER_KEY_SRC_USER;
	pConfig->stChangeFlags.bit1IV = hwCtrl->IVSet;

	return FH_SUCCESS;
}

